package com.rainyun.rc.command;

import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.rainyun.rc.command.annotation.*;
import com.rainyun.rc.ioc.RainIOC;
import com.rainyun.rc.util.RainF;
import com.rainyun.rc.util.RainStr;

public class CmdUtil {

	/**获取所有指令(带CMD注解) 的指令对象<br>
	 * 需要在RainIOC载入所有类后执行
	 * @return Map(指令名,指令对象)*/
	public static Map<String,CmdPath<?>> getAllCmds(List<Class<?>> classes){
		Map<String,CmdPath<?>> cmds = new LinkedHashMap<String, CmdPath<?>>();
		for(Class<?> c:classes) 
		if(c.isAnnotationPresent(BeanCmd.class)){
			String name = c.getAnnotation(BeanCmd.class).value();
			String en = RainIOC.getIOCKey(c);
			CmdPath<?> cmd = (CmdPath<?>)RainIOC.get(en);
			for(Entry<String, Object> en1:getCmdAttr(c).entrySet()){//设置属性
				cmd.setAttr(en1.getKey(),en1.getValue());
			}
			cmds.put(name,cmd);
		}
		return cmds;
	}
	
	/**解析所有指令业务(CmdBiz/Cmd注解)
	 * @param classes 所有相关类
	 * @param maincmds 相关的主要指令(自动根据指令名绑定业务类)
	 * */
	public static void initAllCmdBiz(List<Class<?>> classes,Map<String,? extends CmdPath<?>> maincmds){
		for(Class<?> c:classes)
		{
			String bind = null;
			if(c.isAnnotationPresent(BeanCmdBiz.class)) {
				bind = c.getAnnotation(BeanCmdBiz.class).value();
			}else if(c.isAnnotationPresent(BeanCmd.class)){
				bind = c.getAnnotation(BeanCmd.class).value();
			}else continue;
			String en = RainIOC.getIOCKey(c);
			Object bizObj = RainIOC.get(en);
			CmdPath<?> maincmd = maincmds.get(bind);
			
			List<AccessibleObject> fmds = RainF.filter(c.getDeclaredMethods(),(o)->o.isAnnotationPresent(CmdLine.class));
			List<AccessibleObject> fs = RainF.filter(c.getDeclaredFields(),(o)->o.isAnnotationPresent(CmdLine.class));
			fmds.addAll(fs);
			
			for(AccessibleObject fm:fmds) {
				boolean isMethod = false;
				if(fm instanceof Method) {
					isMethod = true;
				}
				String value = fm.getAnnotation(CmdLine.class).value();
				Map<String,Object> attrs = getCmdAttr(fm);
				
				String[] cmdstra = RainStr.split(value," ",-1);
				CmdPath<?> cmd = maincmd;
				CmdPath<?> tail = null;//最后一个普通节点
				Parameter[] params = isMethod?((Method)fm).getParameters():null;
				int i = 1;
				for(String cmdstr:cmdstra) {
					CmdPath<?> tcmd = cmd.get(cmdstr);
					if(tcmd!=null&&!cmd.isValuePath()) {//不覆盖普通节点
						cmd = tail = tcmd;
						continue;
					}else {
						cmd = cmd.add(cmdstr);
						if(!cmd.isValuePath()) tail = cmd;
					}
					if(isMethod)
					if(cmd.isValuePath()) {
						if(params.length>i) {
							Class<?> type = params[i].getType();
							if(!type.isArray())
								cmd.setType(type);
							else cmd.setType(type.getComponentType());
							i++;
						}else System.out.println("Method "+((Method)fm).getName()+" args length is too short to cmdbiz");
						if(attrs.containsKey("tab")) {//设置tab属性
							cmd.setAttr("tab",attrs.get("tab"));
						}
					}
				}
				if(attrs.containsKey("check")) {//设置check属性
					cmd.setAttr("check",attrs.get("check"));
				}
				if(tail!=null)
				for(Entry<String, Object> en1:attrs.entrySet()) {//设置普通属性
					if(!"check,tab".contains(en1.getKey())) {
						Object v = en1.getValue();
						tail.setAttr(en1.getKey(),v);
					}
				}
				if(isMethod)
				cmd.setComExec((sd,ca)->{
					try {
						Map<String, Object> args = ca.getMap();
						List<Object> toexe = new ArrayList<>(args.values());
						toexe.add(0,sd);
						for(int j=0;j<params.length-toexe.size();j++) {
							toexe.add(null);//超出部分null替换
						}
						fm.setAccessible(true);
						((Method) fm).invoke(bizObj,toexe.toArray(new Object[0]));
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (InvocationTargetException e) {
						throw e.getCause();
					}
				});
			}
			maincmd.sortPaths();
		}
	}

	/**从java反射对象获取对应的CmdAttr指令属性(忽略空字符)*/
	public static Map<String, Object> getCmdAttr(AnnotatedElement fm) {
		Map<String,Object> attrs = new HashMap<String, Object>();
		Class<? extends Annotation> type = null;
		Annotation anno = null;
		for(Annotation ann:fm.getAnnotations()) {
			Class<? extends Annotation> ttype = ann.annotationType();
			if(ttype.equals(CmdAttr.class)||
					ttype.isAnnotationPresent(CmdAttrAnno.class)) {
				anno = ann;type = ttype;
				break;
			}
		}
		if(type!=null)
		for(Method me:type.getDeclaredMethods()) {//设置指令的附加属性
			try {
				me.setAccessible(true);
				String name = me.getName();
				Object mevalue = me.invoke(anno);
				if(mevalue instanceof Enum) {
					mevalue = mevalue.toString().toLowerCase();
				}
				if(name.equals("perm"))
					mevalue = mevalue.toString().replace("_",".");
				if(!mevalue.toString().isEmpty())
				attrs.put(name,mevalue);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return attrs;
	}
	
	/**从树形map为指令集合赋值属性*/
	public static void setAttrByTree(Map<String,? extends CmdPath<?>> maincmds,Map<String,Object> attr_tree,String attr) {
		for(Entry<String, Object> hen:attr_tree.entrySet()) {
			if(maincmds.containsKey(hen.getKey())) {
				CmdPath<?> cmd = maincmds.get(hen.getKey());
				if(hen.getValue() instanceof Map) {
					Map<String, Object> map = (Map<String, Object>) hen.getValue();
					if(map.containsKey("_self"))
						cmd.setAttr(attr,map.get("_self"));
					setAttrByTree(cmd.getSubPaths(),map,attr);
				}else cmd.setAttr(attr,hen.getValue());
			}else continue;
		}
	}
	
	/**获取指令集的指令属性树*/
	public static Map<String,Object> getCmdAttrTree(Map<String, ? extends CmdPath<?>> tree,String attr) {
		Map<String,Object> tm = new LinkedHashMap<>();
		getCmdAttrTree(tree,tm,attr);
		return tm;
	}
	private static void getCmdAttrTree(Map<String, ? extends CmdPath<?>> tree,Map<String,Object> tm,String attr) {
		tree.forEach((k,v)->{
			Map<String, ? extends CmdPath<?>> sm = v.getSubPaths();
			Object attro = v.getAttr(attr);
			
			if(!sm.isEmpty()) {
				Map<String,Object> newm = new LinkedHashMap<String, Object>();
				if(attro!=null)
					newm.put("_self",attro);
				getCmdAttrTree(sm,newm,attr);
				tm.put(k, newm);
			}else {
//				CmdPath<?> sv = v.getSubValuePath();
//				String vstr = "<cmd>";
//				while(sv!=null) {
//					vstr+=" "+sv.getOriginName();
//					CmdPath<?> vp = sv.getSubValuePath();
//					if(vp==null) {
//						Map<String,? extends CmdPath<?>> sp = sv.getSubPaths();
//						if(!sp.isEmpty())sv = sp.entrySet().stream().findFirst().get().getValue();
//						else sv=null;
//					}else sv = vp;
//				}
//				vstr+=" XXX";
//				if(help!=null&&!help.isEmpty())vstr=help;
				if(attro!=null)
					tm.put(k,attro);
			}	
		});
	}

	
}








